线程与进程
进程:在计算中,进程是由一个或多个线程执行的计算机程序的实例。它包含程序代码及其活动。取决于操作系统(OS),一个进程可能由多个并行执行指令的执行线程组成。
线程:在计算机科学中,执行线程是可以由调度程序(通常是操作系统的一部分)独立管理的已编程指令的最小序列。
主要区别:进程是正在执行某些代码的程序,而线程是该进程中执行的独立路径。一个进程可以具有多个用于执行独立任务的线程,例如,一个用于从磁盘读取数据的线程,一个用于处理该数据的线程以及另一个用于通过网络发送该数据的线程。这种提高吞吐量并更好地利用CPU能力的技术也称为多线程。
Thread的常见方法
- start():启动当前线程:调用当前线程内的run()方法
- run():重写Thread()类中的run()方法,方法体为线程需要执行的操作
- currentThread():静态方法,返回执行当前代码的线程
- getName():获取当前线程的名称
- setName():设置当前线程的名称
- yield():静态方法。使当前正在执行的线程向另外一个线程交出运行全
- join():在线程a中调用线程b的join(),此时线程a为阻塞状态,直到线程b完全执行完以后,线程a结束阻塞状态
- sleep():让当前线程睡眠指定的毫秒数(millitime),在指定毫秒内,当前线程是阻塞状态
- isAlive():判断线程是否存活
线程的生命周期
多线程的创建
继承Thread类
- 创建一个Thread类的子类
- 重写run()方法 —> 将线程需要执行的操作写入run()方法体
- 创建Thread类的对象
- 通过此对象调用start()方法
代码实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } }
public class ThreadDemo { public static void main(String[] args) {
Thread.currentThread().setName("主线程");
MyThread1 m1 = new MyThread1(); m1.setName("线程1"); m1.start();
new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2 != 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } }.start(); } }
|
实现Runnable类
- 创建一个实现了Runnable接口的类
- 实现类去实现Runnable接口中的抽象方法:run()
- 将此对象作为参数传递到Thread类的构造器中,并创建对象
- 通过Thread类的对象调用start()方法
代码实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class MThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i); } } } }
public class ThreadTest1 { public static void main(String[] args) { MThread mThread = new MThread(); Thread t1 = new Thread(mThread); } }
|
实现Callable类
该方式创建多线程比Runnable方法更强大:
- call()方法可以有返回值
- call()方法可以抛出异常
- 创建一个实现Callable的实现类
- 实现call方法,并将线程需要执行的操作声明在方法体内
- 创建Callable接口实现类的对象
- 将Callable接口实现类的对象作为参数传递到FutureTask构造器中,并创建FutureTask的对象
- 将FutureTask的对象传递到Thread类的构造器中,创建Threa类的对象,并调用start()方法
- 获取Callable中call方法的返回值
代码实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class NumThread implements Callable { @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) { NumThread numThread = new NumThread();
FutureTask futureTask = new FutureTask(numThread);
new Thread(futureTask).start();
try { Object sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
|
使用线程池
好处:
- 提高响应速度(减少创建新线程的时间)
- 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
- 便于线程管理
- 提供指定线程数量的线程池
- 执行指定线程的操作。提供Runnable或者Callable接口实现类的对象
- 关闭线程池
代码实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if(i%2 == 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } }
public class ThreadPool { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
service.execute(new NumberThread());
service.shutdown(); } }
|
java 创建线程的三种方式、创建线程池的四种方式
线程安全与同步
当多个线程访问某个类时,该类始终都表现正确行为,则称该类是线程安全的。
在多线程访问环境中,对于共享数据的访问引起数据的不一致性,则此时是线程不安全的。需要考虑线程同步,来处理并发访问带来的问题。
synchronized
同步代码块
1 2 3
| synchronized(同步监视器){ }
|
- 需要同步的代码:操作共享数据的代码,不可以包含多,也不可以包含少。
- 共享数据:多个线程共同操作的变量。
- 同步监视器:俗称:锁。任何一个类的对象都可以充当锁。但是要求多个线程必须共用一把锁。
实现Runnable接口
由于采用实现Runnable接口的方式,只需要创建一个实现Runnable接口的实现类的对象即可,所以在同步时,同步监视器填写的最简单方式为this。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Window1 implements Runnable {
private int ticket = 100;
@Override public void run() { while(true){ synchronized(this){ if(ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + ": 卖票,票号为:" + ticket); ticket--; }else{ break; } } } } }
public class WindowsTest1 { public static void main(String[] args) { Window1 w = new Window1();
Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w);
t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3");
t1.start(); t2.start(); t3.start();
}
}
|
继承Thread类
由于采用继承Thread的方式,所以n个线程就需要建n个对象实现多线程。调用继承Thread继承类时,会重复造对象。所以里面的共有数据必须是静态的。在进行synchronized同步代码块时,同步监视器必须是静态对象,不能是this!除此之外也可以是继承类.class
返回Class类型的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class WindowTest2 { public static void main(String[] args) { Window2 t1 = new Window2(); Window2 t2 = new Window2(); Window2 t3 = new Window2();
t1.setName("window-1"); t2.setName("window-2"); t3.setName("window-3");
t1.start(); t2.start(); t3.start(); }
}
class Window2 extends Thread{
private static int ticket = 100; private static Object obj = new Object();
@Override public void run() { while(true){ synchronized (Window2.class){ if(ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(getName() + ": 卖票,票号为: " + ticket); ticket--; }else{ break; } } } } }
|
同步方法
同样使用synchronized关键字,在同步方法中,用来修饰方法。
然后在run()方法中调用该方法。
1 2 3
| private synchronized void show(){ // 方法体 }
|
继承Thread类
此时对应的同步监视器为对应继承类的Class类对象
实现Runnable接口
此时对应的同步监视器为this(实现Runnable接口的实现类的对象)
Lock
和synchronized不同的是,Lock需要手动的进行加锁和解锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class Window implements Runnable{ private int ticket = 100; private ReentrantLock lock = new ReentrantLock(true);
@Override public void run() { while(true){ try { lock.lock();
if(ticket > 0){ System.out.println(Thread.currentThread().getName() + ": 售票,票号为:" + ticket); ticket--; }else { break; } } finally { lock.unlock(); } } } }
public class LockTest { 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("窗口1"); t2.setName("窗口2"); t3.setName("窗口3");
t1.start(); t2.start(); t3.start(); } }
|
单例设计模式之懒汉式(线程安全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public class BankTest {
}
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;
} }
|
这种写法能够在多线程中很好的工作,但是每次调用getInstance()方法时都需要进行同步,造成不必要的同步开销。
参考: 单例模式(饿汉模式、懒汉模式(线程安全和线程不安全))