1.程序、进程、线程:
- 程序:是指为实现一定功能的由计算机语言编写的一组指令的集合,可理解为静态的代码;
- 进程:可以理解为一个正在执行的程序或程序的一次执行过程,它具有生命周期;进程作为资源分配的单位,运行在系统时会为每个进程分配不同的内存区域;
- 线程:线程可以理解为进程的进一步细分,一个进程可以有多个线程;(一个 java.exe至少有三个线程:main方法主线程、GC垃圾回收线程、异常处理线程)
2.创建线程
- 方式①
继承Thread类重写run方法:
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
//让当前线程休眠10毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+"====="+Thread.currentThread().getName());
}
}
}
public class ThreadTest {
public static void main(String[] args){
//主方法线程循环打印0-499
for (int i = 0; i < 500; i++) System.out.println(i+"====="+Thread.currentThread().getName());
//创建自定义线程对象
MyThread thread = new MyThread();
//重新命名自定义线程
thread.setName("MyThread");
//设置线程优先级
thread.setPriority(Thread.MAX_PRIORITY);
//启动自定义线程
thread.start();
}
- 方式②
实现Runnable接口重写run方法:
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) System.out.println(i+"====="+Thread.currentThread().getName())
}
}
public class ThreadTest {
public static void main(String[] args){
for (int i = 0; i < 500; i++) System.out.println(i+"====="+Thread.currentThread().getName());
Thread thread1=new Thread(new MyRunnable(),"MyRunnable");
thread1.setPriority(Thread.MAX_PRIORITY);
thread1.start();
}
}
- 方式③
实现Callable接口重写call方法:
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception { //返回类型为Integer
Thread.sleep(100);
int sum = 0;
for(int i=0;i<100;i++) sum += i;
return sum;
}
}
public class FutureTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool(); //构造线程池
MyCallable task = new MyCallable();
Future<Integer> result = executor.submit(task); //获取任务执行结果
executor.shutdown();
try {
System.out.println("task运行结果"+result.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
- tips:
①线程在启动【start()】之后会执行继承了Thread的类中的run方法中的代码
②实现了Runnable接口意味着该类具有可执行性,但该实现类不是线程类
③多个线程同时执行,它们之间是互不干扰的,并且线程的执行具有随机性
④线程有十个优先级(1-10)但设置的线程优先级高的不一定会被CPU优先执行
⑤继承Thread类和实现Runnable接口两种方式的比较:- Thread类实现了Runnable接口(联系)
- Runnable接口解决了单继承的局限性
- 当有共享数据时,使用实现方式更好,同时共享数据所在的类可以作为Runnable接口的实现类
3.线程的状态:
- 创建:new Thread()//创建线程对象,并给线程设置默认名称
- 就绪:当线程对象调用start()方法时,线程进入就绪状态,该线程状态表示可被执行,并不一定立即被执行;
- 运行:当CPU调度到某线程时,该线程处于运行状态
- 阻塞:sleep()、wait()、join()、suspend()【过时】、线程同步锁【当线程进入阻塞状态时,会暂时让出CPU资源】
- 死亡:正常执行完run()/main()方法、线程对象调用stop()、Error/Exception未被处理;
- 线程状态图:
4.线程的同步机制
- 前提:如果我们创建的多线程中有存在着共享数据,那么就有可能出现出现线程安全问题:当其中一个线程操作共享数据还未结束时,另一个线程参与进来,导致共享数据的操作出现问题。
- 解决方法:要求一个线程完成对共享资源的操作之后,其他的线程才能操作共享资源
-
方式一:同步代码块:
synchronized( 同步监视器 ){ //操作共享数据的代码 }- 注:
①同步监视器:俗称锁,任何一个类的对象都可以才充当锁。要想保证线程的安全,必须要 求所有的线程共用同一把锁!
②使用实现Runnable接口的方式创建多线程的话,同步代码块中的锁,可以考虑是【this】。如 果使用继承Thread类的方式,不用this,可以考虑【类名.class】
③共享数据:多个线程需要共同操作的变量。 明确哪部分是操作共享数据的代码。
- 注:
-
方式二:同步方法:
将操作共享数据的方法声明为synchronized。
比如:public synchronized void show(){ //操作共享数据的代码}
注:①.对于非静态的方法而言,使用同步的话,默认锁为:this。如果使用在继承的方式实现多线程 的话,慎用this!
②静态的方法如果使用同步,默认的锁为:当前类本身。 -
tips:
- 释放锁:wait()
- 不释放锁:sleep()、yield()
- 死锁:不同的线程分别占用对方的同步资源不放弃,都在等对方放弃自己需要的同步资源,这就形 成了死锁(死锁无法解决,只能避免)例:
-
public class DeadLock {
private static String Stephen="Stephen";
private static String Curry="Curry";
public static void main(String[] args){
new DeadLock().deadLock();
}
private void deadLock(){
Thread threadA=new Thread(new Runnable(){
@Override
public void run(){
synchronized(Stephen){
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(Curry){
System.out.println("AB");
}
}
}
}).start();
Thread threadB=new Thread(new Runnable(){
@Override
public void run(){
synchronized(Curry){
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(Stephen){
System.out.println("BA");
}
}
}
}).start();
}
}
5.线程的通信
①两个线程交替打印1-100自然数
public class CommunicationTest {
public static void main(String[] args) {
Print print = new Print();
Thread thread1 = new Thread(print);
Thread thread2 = new Thread(print);
thread1.setName("线程1打印:");
thread1.start();
thread2.setName("线程2打印:");
thread2.start();
}
}
class Print implements Runnable {
public static int num = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
if (num <= 100) {
//唤醒另外一个线程
this.notify();
System.out.println(Thread.currentThread().getName()+(num++));
try {
wait(); //该线程释放资源进去阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
} else break;
}
}
}
}
②生产者与消费者
public class TestProductAndCustomer {
public static void main(String[] args) {
Waitter waitter = new Waitter();
Productor p = new Productor(waitter);
Customer c1 = new Customer(waitter);
Customer c2 = new Customer(waitter);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
class Waitter {
private int produce = 0;
public synchronized void produce() {
if (produce < 20) {
produce++;
System.out.println(Thread.currentThread().getName() +
"生产了第" + produce + "个产品");
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void custom() {
if (produce > 0) {
System.out.println(Thread.currentThread().getName() +
"消费了第" + produce + "个产品");
produce--;
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Productor implements Runnable {
private Waitter waitter;
public Productor(Waitter waitter) {
this.waitter = waitter;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitter.produce();
}
}
}
class Customer implements Runnable {
private Waitter waitter;
public Customer(Waitter waitter) {
this.waitter = waitter;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitter.custom();
}
}
}
tips:
线程的通信:如下的三个方法必须使用在同步代码块或同步方法中:
-
wait():当在同步中,执行到此方法,则此线程“等待”,直至其他线程执行notify()的方法,将其唤醒,唤醒后继续其wait()后的代码
-
notify()/notifyAll():在同步中,执行到此方法,则唤醒其他的某一个或所有的被wait的线程。
-
线程安全的懒汉式:
class SingleTon{
private static SingleTon instance;
private SingleTon() {}
public static SingleTon1 getIntance(){
if (instance==null){
synchronized (SingleTon1.class){
if(instance==null)
return instance=new SingleTon1();
}
}
return instance;
}
}