多线程
多线程
一、线程和进程的区别
-
进程:进程是指开启一个应用程序,即一个服务。进程是一个具有一定独立功能的程序,在一个数据集上的一次动态执行过程
是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。一般由程序、数据集合、进程控制块三部分组成。
-
线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单元。一个进程可以有一个或者多个线程。

二、线程的一般方法
-
线程的创建
- 继承Thread类
- 实现Runnable接口
方式一:继承Thread类
public class testThread extends Thread{ public class testThread(String name){ super(name); } public void run(){ System.out,printf(getName()+"线程执行了"); } public static void main(String[]agrs){ Thread t1 = new testThread("李白"); t1.start(); } }方式二:实现Runnable接口
public class testThread2 implements Runnable{ private String name; public testThread2(String name){ this.name = name; } @Override public void run(){ System.out.printf(Thread.CurrentThread.getName()+"线程执行了"); } public static void main(String[]args){ testThread2 test = new testThread2("白居易"); Thread t2 = new Thread(test,test.name); t2.start(); } }还可以通过匿名类的方法来实现
public class testThread3 { public static void main(String[]args){ Thread t3 = new Thread(){ public void run(){ System.out.printf(Thread.CurrentThread.getName()+"线程运行了"); } }; t3.start(); } Thread t4 = new Thread(new Runnable{ @Override public void run(){ System.out.printf(Thread.CurrentThread.getName()+"线程执行了"); } },"白居易"); t4.start(); }- 线程中的一般方法
* sleep() 睡眠方法 执行此方法,使当前线程睡眠一段时间,用于减缓程序执行的速度 * setPriority() 设置线程的优先级 线程的执行一般是按照优先级的大小来执行。线程的优先级为一个1~10的整数,默认提供的优先级是5,最小的优先级为MinPriority = 1 MaxPriority = 10; * yield() 对调度线程的一个暗示,即当前线程愿意放弃当前cup的资源让与之同级的线程能够执行,这是保证线程执行的最佳方案。 * join()等待这个线程死亡 即两个线程A、B 如果 Ajoin(B) 那么会先执行B线程,当B线程执行完毕后A线程才会执行。三、线程的同步
-
线程同步的方式
-
通过Object的wait和notify方式 配合 synchronized线程同步锁实现
public static boolean flag = false; public static int num = 0; public static void main(String[]args){ Man man = new Man(); new Thread(() ->{ man.getRunnable1(); }).start(); new Thread(() ->{ man.getRunnable2(); }).start(); } public static class Man{ public synchronized void getRunnable1(){ for(int i = 0;i<20;i++){ while(flag){ try{ wait(); }catch(InterruptedException e){ e.printStackTrace(); } } System.out.println("生产出:"+(++num)+"个"); flag = true; norify(); } } public synchronized void getRunnable2(){ for(int i = 0;i<20;i++){ while(!flag){ try{ wait(); }catch(InterruptedException e){ e.printStackTrace(); } } //模拟加载时间 try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("取出:"+(num--)+"个"); System.out.println("---------------"); flag = false; notify(); } } }2、通过Condition的await和signal 配合 lock锁实现
public static boolean flag = false; public static int num = 0; public static void main(String[]args){ Man man = new Man(); new Thread(() ->{ man.getRunnable1(); }).start(); new Thread(() ->{ man.getRunnable2(); }).start(); } public static class Man{ public static ReentrantLock lock = new ReentrantLock(); public static Condition condtion = lock.newCondition(); public void getRunnable1(){ lock.lock(); try{ for(int i = 0;i<20;i++){ while(flag){ try{ condition.await(); }catch(InterruptedException e){ e.printStackTrace(); } } System.out.println("生产出:"+(++num)+"个"); flag = true; condition.signal(); } }finally{ lock.unlock(); } } public synchronized void getRunnable2(){ lock.lock(); try{ for(int i = 0;i<20;i++){ while(!flag){ try{ condition.await(); }catch(InterruptedException e){ e.printStackTrace(); } } //模拟加载时间 try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("取出:"+(num--)+"个"); System.out.println("---------------"); flag = false; condition.signal(); } }finally{ lock.unlock(); } } }3、通过阻塞队列实现
public static class Man { ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(64); public void getRunnable1() { for (int i = 0; i < 8; i++) { System.out.println("生产出:" + i + "个"); try { queue.put(i); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("---------------生产完毕-----------------"); } public void getRunnable2() { for (int i = 0; i < 8; i++) { try { int num = (int) queue.take(); System.out.println("取出出:" + num); } catch (InterruptedException e) { e.printStackTrace(); } } } } 很明显使用阻塞队列代码精炼了很多,在这还可以发现这个阻塞队列是具有缓存功能的,想很多Android中网络访问框架内部就是使用这个进行缓存的,例如Volley、Okhttp等等。 2、通过两个阻塞队列 使用一个阻塞队列能够实现线程同步的功能,两个阻塞队列也可以实现线程同步。原理是ArrayBlockingQueue他是具有容量的,如果把他的容量定位1则意味着他只能放进去一个元素,第二个方进行就会就会被阻塞。按照这个原理进行来实现,定义两个容量为1的阻塞队列ArrayBlockingQueue,一个存放数据,另一个用于控制次序。main方法和上面一致,主要来看看Man类中的两个方法: static class Man { //数据的存放 ArrayBlockingQueue queue1 = new ArrayBlockingQueue<Integer>(1); //用于控制程序的执行 ArrayBlockingQueue queue2 = new ArrayBlockingQueue<Integer>(1); { try { //queue2放进去一个元素,getRunnable2阻塞 queue2.put(22222); } catch (InterruptedException e) { e.printStackTrace(); } } public void getRunnable1() { new Thread(() -> { for (int j = 0; j < 20; j++) { try { //queue1放进一个元素,getRunnable1阻塞 queue1.put(j); System.out.println("存放 线程名称:" + Thread.currentThread().getName() + "-数据为-" + j); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } try { //queue2取出元素,getRunnable2进入 queue2.take(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } public void getRunnable2() { new Thread(() -> { for (int j = 0; j < 20; j++) { try { //queue2放进一个元素,getRunnable2阻塞 queue2.put(22222); } catch (InterruptedException e) { e.printStackTrace(); } try { //queue1放进一个元素,getRunnable1进入 int i = (int) queue1.take(); System.out.println("获取 线程名称:" + Thread.currentThread().getName() + "-数据为-" + i); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } 再次提醒queue2用于控制程序的执行次序,并无实际含义。最后看看运行效果,存一个、取一个很清晰,如下: 3、通过SynchronousQueue SynchronousQueue不同于一般的数据等线程,而是线程等待数据,他是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。通过这一特性来实现一个多线程同步问题的解决方案,代码如下: /** * 使用阻塞队列SynchronousQueue * offer将数据插入队尾 * take取出数据,如果没有则阻塞,直到有数据在获取到 */ public static void test() { SynchronousQueue queue = new SynchronousQueue(); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(5000); queue.offer(9); } catch (InterruptedException e) { e.printStackTrace(); } } }); try { int take = (int) queue.take(); System.out.println(take); } catch (InterruptedException e) { e.printStackTrace(); } } 子线程中进行设置数据,而主线程获取数据,如果子线程没执行完毕,子线程没有执行完毕主线程就会被阻塞住不能执行下一步。 六、通过线程池的Callback回调 在线程的创建中,有一种创建方法可以返回线程结果,就是callback,他能返回线程的执行结果,通过子线程返回的结果进而在主线程中进行操作,也是一种同步方法,这种同步在Android中特别适用,例如Android中的AsyncTask源码中任务的创建部分。代码如下: private static void test() { ExecutorService executorService = Executors.newFixedThreadPool(5); Future<Boolean> submit = executorService.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return false; } }); try { if (submit.get()) { System.out.println(true); } else { System.out.println(false); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } 4、通过同步辅助类CountDownLatch CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。他类实际上是使用计数器的方式去控制的,在创建的时候传入一个int数值每当我们调用countDownt()方法的时候就使得这个变量的值减1,而对于await()方法则去判断这个int的变量的值是否为0,是则表示所有的操作都已经完成,否则继续等待。可以理解成倒计时锁。 public class Test7 { public static void main(String[] args) { //启动两个线程,分别执行完毕之后再执行主线程 CountDownLatch countDownLatch = new CountDownLatch(2); //线程1执行 Thread thread1 = new Thread(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "线程执行完毕"); countDownLatch.countDown(); }); //线程2执行 Thread thread2 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "线程执行完毕"); countDownLatch.countDown(); }); thread1.start(); thread2.start(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //执行主线程 System.out.println("主线程执行完毕"); } } 5、通过同步辅助类CyclicBarrier CyclicBarrier是一个同步的辅助类,和上面的CountDownLatch比较类似,不同的是他允许一组线程相互之间等待,达到一个共同点,再继续执行。可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。 public class Test8 { public static void main(String[] args) { //启动两个线程,分别执行完毕之后再执行主线程 CyclicBarrier barrier = new CyclicBarrier(2, () -> { //执行主线程 System.out.println("主线程执行完毕"); }); //线程1执行 Thread thread1 = new Thread(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "线程执行完毕"); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }); //线程2执行 Thread thread2 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "线程执行完毕"); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }); thread1.start(); thread2.start(); } }
-

浙公网安备 33010602011771号