首页 > 编程语言 > 详细

java多线程基础(尚硅谷.宋红康)

时间:2020-10-08 22:24:53      阅读:52      评论:0      收藏:0      [点我收藏+]

java多线程基础(尚硅谷.宋红康)

对程序、进程和线程的理解

程序:为完成特定任务、用某种语言编写的一组指令的集合。一段静态的代码。

进程:是程序的一次执行过程,动态的正在运行的程序(进程是资源分配的最小单元)。

线程:程序内部的一条执行路径(线程是CPU调度的最小单元)。

Thread中常用方法

  1. start():启动当前线程;调用当前线程的run方法()

  2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

  3. currentThread():静态方法,返回执行当前代码的线程

  4. getName():获取当前线程的名字

  5. setName():设置当前线程的名字

  6. yield():释放当前cpu()的执行权

  7. join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态

  8. stop():已过时,不推荐使用,强制结束当前线程。

  9. sleep(long millitime):让当前线程睡眠(阻塞)指定时间(毫秒)。

  10. isAlive():判断当前线程是否存活。

线程的优先级

  1. 线程的优先级:

    MAX_PRIORITY:10

    MIN_PRIORITY:1

    NORM_PRIORITY:5

  2. 涉及的方法:

    getPriority():返回线程优先值

    setPriority(int newPriority()):改变线程的优先级

  3. 说明:

    线程创建时继承父线程的优先级

    低优先级只是获得调度的概率低,并非一定是在高优先级之后才被调用

创建多线程的两种基本方式

/**
* 多线程的创建,方式一:继承Thread类
* 1,创建一个继承Thread类的子类
* 2,重写Thread类的run方法
* 3,创建Thread类的子类的对象
* 4,通过此对象调用start()
*
* 示例: 遍历100以内的所有偶数
* */
public class Thread1 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
//启动当前线程;调用当前线程的run方法。
t1.start();
}
}
}
?
?
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 100; i++) {
if(i%2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
?
/**
* 创建多线程的方式二:实现Runnable接口
* 1.创建一个实现了Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run()
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start()
* */
public class Thread2 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
Thread t1 = new Thread(myThread2);
t1.start();
}
}
?
class MyThread2 implements Runnable{
@Override
public void run() {
for(int i = 0; i < 100; i++) {
if(i%2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
?

比较以上两个创建方式:

开发中:优先选择:实现Runnable接口的方式

原因:1,实现的方式没有类的单继承性的局限

2,实现的方式更适合来处理多个线程有共享数据的情况

 

线程安全问题

一,通过Java同步机制解决线程安全问题

方式一:同步代码块
synchronized(同步监视器){
   //需要同步的代码(涉及到操作共享数据)
}

*同步监视器:俗称“锁”,任何一个类的对象都可以充当锁,多个线程共用一个锁--唯一性

方式二:同步方法
/**
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
在方法体前加synchronized
*/
单例模式之懒汉式
class Bank{
   private static Bank instance = null;
   
   public static Bank getInstance(){
       if(instance == null){
           synchronized(Bank.class){
               if(instance == null){
                   instance = new Bank();
              }
          }
      }
       return instance;
  }
}
死锁

不同的线程分别占用对方需要的同步资源不放弃,都等待对方放弃自己需要的同步资源。

方式三:Lock锁
package MyThread;
?
import java.util.concurrent.locks.ReentrantLock;
?
/**
* 解决线程安全问题方式三:Lock锁
*/
?
class Window implements Runnable{
private int ticket = 100;
//1,创建ReentrantLock对象
private ReentrantLock lock = new ReentrantLock();

@Override
public void run() {
while(true) {
try {
//2调用lock()方法
lock.lock();
if(ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 售票, 票号为: " + ticket);
ticket--;
}else {
break;
}
}finally {
//3调用unlock()方法
lock.unlock();
}

}
}
}
?
?
public class Lock {
public static void main(String[] args) {
Window w = new Window();

Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);

t1.setName("一号");
t2.setName("二号");
t3.setName("三号");

t1.start();
t2.start();
t3.start();
}
}
?

Q:Lock()锁有同步监视器吗?

A:如果使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用wait(),notify(),notifyAll()方法进行线程通信了;Lock内没有同步监视器Lock的实现是基于AQS(AbstractQueuedSynchronizer)实现的,其实就是一个简单的类,只是利用了优秀的设计来实现了锁。wait,notify,notifyAll调用的前提都得获得对象监视器,通常用synchronize来获取监视器。

 

线程通信

示例:两个线程交替打印数字1-100

package MyThread;
?
import java.util.concurrent.locks.ReentrantLock;
?
class Number implements Runnable{

private int number = 1;

@Override
public void run() {
while(true) {
synchronized(this) {
                   //唤醒其他线程
notify();//notifyAll();
                   //this.notify();
if(number <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
                           //释放锁
wait();
                           //this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
?
public class Communication {
public static void main(String[] args) {
Number number = new Number();

Thread t1 = new Thread(number);
Thread t2 = new Thread(number);

t1.setName("线程1");
t2.setName("线程2");

t1.start();
t2.start();
}
}
?

note:wait(),notify(),notifyAll()三个方法必须使用在同步代码块和同步方法中(syncronized),且这三个方法的调度者必须是同步代码块或同步方法中的同步监视器,这三个方法是定义在java.lang.Object类中

Q:sleep()和wait()的异同

A:相同点:一旦执行,都可以使得当前线程进入阻塞状态。

不同点:1>两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()

2>调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码或同步方法中。

3>sleep()不会释放锁,wait()会释放锁。

 

/**
线程通信的经典例题:生产者/消费者问题
生产者(Producer):生产产品交给店员;店员(Clerk):持有一定上限数量的产品,若产品持有量达到上限叫停成产,没有产品则暂停供应;消费者(Customer):从店员处消费产品。
*/
package MyThread;
?
class Clerk{

private int productCount= 0;
?
public synchronized void produceProduct() {
// 生产
if(productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + "生产第" + productCount + "个产品");

notify();

}else {
//等待
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
?
public synchronized void consumeProduct() {
// 消费
if(productCount > 0) {
System.out.println(Thread.currentThread().getName() + "消费第" + productCount + "个产品");
productCount--;

notify();

}else {
//等待
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
?
class Producer extends Thread{

private Clerk clerk;

public Producer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 开始生产......");
while(true) {
clerk.produceProduct();
}
}
}
?
class Consumer extends Thread{

private Clerk clerk;

public Consumer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 开始消费......");
while(true) {
clerk.consumeProduct();
}
}
}
?
?
public class Productor {
public static void main(String[] args) {
Clerk clerk = new Clerk();

Producer p1 = new Producer(clerk);
p1.setName("生产者1");

Consumer c1 = new Consumer(clerk);
c1.setName("消费者1");

p1.start();
c1.start();
}
}

JDK5.0新增线程创建方式

新增方式一:实现Callable接口

新增方式二:使用线程池

package MyThread;
?
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
?
/**
* 创建线程的方式三:实现Callable接口 ---JDK5.0新增
* FutureTask是Future接口的唯一实现类,同时实现Runnable和Fure接口,既可作为Runnable被线程执行,又可作为Future得到Callable的返回值。
* */
?
//1,创建一个实现Callable的实现类
class NumThread implements Callable{
//2,实现call方法,将此线程需要执行的操作声明在call()中,call()可以有返回值
@Override
public Object call() throws Exception{
int sum = 0;
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println(i);
sum += i;
}
}
return sum;
}

}
?
?
public class ThreadNew {
public static void main(String[] args) {
//3,创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4,将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5,将FutureTask的对象作为参数传递到Thread类构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
//6,获取返回值(可选)
try {
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("可取的返回值"+sum);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
?

如何理解Callable接口的方式创建多线程比实现Runnable接口创建多线程强大:

1,call()可以有返回值

2,call()可以抛出异常

3,支持泛型(待补充......)

package MyThread;
?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
?
/**
* 创建多线程的方式四:使用线程池
* ExecutoService和Executors
*/
?
class NumberThread implements Runnable{
?
@Override
public void run() {
for(int i = 1; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
?
class NumberThread1 implements Runnable{
?
@Override
public void run() {
for(int i = 1; i <= 100; i++) {
if(i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
?
public class ThreadPool {
public static void main(String[] args) {
//1,提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//2,执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合Runnable
service.execute(new NumberThread1());
//service.submit();//适合Callabel
//3,关闭连接池
service.shutdown();
}
}

提高响应速度(减少了创建新线程的时间)

降低资源消耗(重复利用线程池中线程,不需要每次创建)

便于管理

 

思考

1,线程的生命周期

2,同步代码快中涉及到同步监视器和共享数据,谈谈你对同步监视器和共享数据的理解,以及注意点。

同步监视器必须唯一;共享数据需要同步(线程安全)。

3,sleep()和wait()的区别。

4,写一个线程安全的懒汉式。

5,创建多线程有哪几种方式。

java多线程基础(尚硅谷.宋红康)

原文:https://www.cnblogs.com/hzjin/p/13782489.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!