一、了解线程、进程之间的关系
进程:说起进程,不得不提起程序。程序是指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念。
线程:而线程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
通常一个进程中包含一个或多个线程。线程是CPU调度和执行的单位。
二、创建线程的三种方式
1)继承Thread类,并重写run方法
public class ThreadTest extends Thread {
@Override
public void run() {
//run方法线程主体
for (int i = 0; i < 100; i++) {
System.out.println("++线程内执行次数:" + i);
}
}
public static void main(String[] args) {
//启动线程
ThreadTest threadTest = new ThreadTest();
threadTest.start();
for (int i = 0; i < 100; i++) {
System.out.println("--线程外执行次数:" + i);
}
}
}
2)实现Runnable接口,重写run方法
//创建线程方式2 : 实runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,启用start方法.
public class TestThread1 implements Runnable {
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("++线程内执行次数:" + i);
}
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
Thread thread = new Thread(testThread1);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("--线程外执行次数:" + i);
}
}
}
3)实现Callable接口
- 1.实现Callable接口,需要返回值类型
- 2.重写call方法,需要抛出异常
- 3.创建目标对象
- 4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
- 5提交执行: Future result1 = ser.submit(t1);
- 6…获取结果: boolean r1 = result1.get()
- 7.关闭服务: ser.shutdownNow();
三、线程状态
四、线程停止、休眠、礼让、强制执行
1)线程停止:
/**
* 使用状态标记来停止线程
*/
public class TestThreadDemo01 implements Runnable{
public static void main(String[] args) throws Exception {
TestThreadDemo01 threadDemo01 = new TestThreadDemo01();
new Thread(threadDemo01).start();
for (int i=0;i<1000;i++){
System.out.println("主线程"+i);
if (i==900){
threadDemo01.stop();
System.out.println("线程停止了!");
break;
}
}
}
//状态标记
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("线程!" + (i++));
}
}
public void stop(){
this.flag = false;
}
}
2)线程休眠
public class TestThread1 implements Runnable {
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 200; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("++线程内执行次数:" + i);
}
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
Thread thread = new Thread(testThread1);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("--线程外执行次数:" + i);
}
}
}
重点:
- sleep(时间)指定当前线程阻塞的毫秒数;
- sleep存在异常InterruptedException
- sleep时间到达后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等。
- 每一个对象都有一个锁,sleep不会释放锁
3)线程礼让
/**
* 线程强制执行---插队
*/
public class TestThreadJoin implements Runnable {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new TestThreadJoin());
for (int i = 0; i < 500; i++) {
System.out.println("主线程在执行中---" + i);
if (i == 100) {
thread.start();
thread.join();
}
}
}
@Override
public void run () {
for (int i = 0; i < 100; i++) {
System.out.println("VIP线程来插队啦" + i);
}
}
}
重点:
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
五、线程同步锁
同步方法:由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入 锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待使用后释放锁即可存在以下问题
-
一个线程持有锁会导致其他所有需要此锁的线程挂起
-
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时起性能问题;
-
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
class BuyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;
//外部停止方式
@Override
public void run() {
//买票
while (flag) try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//synchronized 同步方法,
private synchronized void buy() throws InterruptedException {
//判斯是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "我").start();
new Thread(station, "黄牛").start();
new Thread(station, "你").start();
}
}
同步块
同步块: synchronized (Obj){}
- obj 称之为 同步监视器
- obj 可以是任何对象,但是推荐使用共享资源作为同步监视器同步方法中无需指定同步监视器,因为同 - 步方法的同步监视器就是this,就是这个对象本身,或者是 class[反射中讲解]
- 同步监视器的执行过程
1.第一个线程访问,锁定同步监视器,执行其中代码
2.第二个线程访问,发现同步监视器被锁定,无法访问3. 第一个线程访问完毕,解锁同步监视器
4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问
六、Lock锁
-
从JDK 5.0开始,Java提供了更强大的线程同步机制一通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
-
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。