目录
1、线程基本知识:
2、Thread方式创建线程
3、Runnable接口方式创建线程
4、线程安全问题
4.1、同步代码块
4.2、synchronized修饰
4.3、Lock锁
/*
1、并发并行:
并发:两个或多个事件在同一时间段内发生
多个事件交替执行
并行:两个或多个事件在同一时刻发生
多个事件同时执行
并行速度快些
2、进程与线程:
线程属于进程,是进程的一个执行单元,负责程序的执行
进程:点击程序,会进入内存中占用内存执行,进入到内存的程序叫做进程
线程:点击程序功能执行,就会开启一条应用程序到cpu的执行路径,cpu就可以通过这个路径执行功能,该路径叫线程。
cpu:中央处理器,对数据进行计算,指挥电脑中的软硬件干活
分类:AMD
Inter Inter Core(核心) i7 8866 4核心8线程
8线程可以同时执行8个任务
单核心单线程cpu:cpu在多个线程之间高速切换,轮流执行多个线程,效率低,切换速度(1/n毫秒)
4核心8线程cpu:可以同时执行8个线程,8个线程在多个人物之间做高速的切换,速度是单线cpu的8倍(每个任务执行到的几率被提高8倍)
多线程好处:1、效率高 2、多个线程之间互不影响
3、线程调度:
分时调度:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间
抢占式调度:优先让优先级高的线程使用cpu【抢占cpu占用时间】,如果线程优先级相同,那么随机选择一个线程。【java使用抢占式调度】
4、主线程:
执行主方法(main)的线程
单线程程序:java程序中只有一个线程,执行从main方法开始,从上到下依次执行【出错后面就执行不了了】
概念:JVM执行main方法进入到栈内存,JVM就会找操作系统开辟一条main方法通向cpu的执行路径,cpu就可以通过这个路径来执行main方法
这个路径就叫主(main)线程
*/
/*
创建多线程:
方式1:
创建Thread类的子类
java.lang.Thread类,是描述线程的类
实现:
1、创建Thread类的子类
2、在Thread的子类重写Thread类中的run方法,设置线程任务【开启线程要做什么】
3、创建Thread类的子类对象
4、调用Thread类的方法start方法,开启新的线程,执行run方法【中的任务】
public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和创建的新线程(执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
java程序属于抢占式调度。
主方法调用一次start()方法就是开辟新的栈空间,也就是增加一个线程,执行run()方法。多个线程互不影响。然后cpu就有了选择栈优先级的权利。
*/
//方式1、
public class Thread_lianxi extends Thread {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("run:"+i);
}
}
}
public class Thread_lianxi01 {
public static void main(String[] args) {
Thread_lianxi t=new Thread_lianxi();
t.start(); //和直接调用run方法,是有区别的【直接调用run(),是一个单线程】
for(int i=0;i<5;i++){
System.out.println("main:"+i);
}
}
}
//java使用抢占式调度,优先级相同,所以随机选一个先执行
main:0
run:0
main:1
run:1
main:2
run:2
main:3
run:3
main:4
run:4
/*
* 一、获取线程的名称:
* 1、获取Thread类中的方法 String getName()
* 2、可以先获取当前正在执行的线程,使用线程中的方法getName()获取线程名称
* static Thread currentThread() 返回对当前正在执行的线程对象的引用。
二、设置线程名称【了解】:
1、使用Thread类中的setName(线程名字) void setName(String name) 改变线程名称,使之与参数 name 相同。
2、创建一个带参数的构造方法,参数传递线程名称;调用父类的带参构造方法,把线程名称传递给父类,让父类给子线程起一个名字。
Thread(String name) 分配新的 Thread 对象。
* */
//一
public class Thread_lianxi02 extends Thread {
@Override
public void run() {
//1
// String name = getName();
//System.out.println(name);//Thread-0 Thread-1
Thread th=new Thread();
System.out.println(th);//Thread[Thread-2,5,main] Thread[Thread-3,5,main]
String name = th.getName();
System.out.println(name);//Thread-3 Thread-2
//2 链式编程
System.out.println(Thread.currentThread().getName());//
//Thread-2 Thread-0
}
}
public class Thread_lianxi03 {
public static void main(String[] args) {
Thread_lianxi02 t=new Thread_lianxi02();
t.start();
new Thread_lianxi02().start();
System.out.println(Thread.currentThread().getName());
//main
}
}
==
//二、
public class MyThread_lianxi extends Thread {
public MyThread_lianxi(){}
public MyThread_lianxi(String name){
super(name);//将线程名称传递给父类,让父类给子线程起一个名字
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class MyThread_lianxi01 {
public static void main(String[] args) {
MyThread_lianxi mt=new MyThread_lianxi();
mt.setName("小强");
mt.start();//小强
new MyThread_lianxi("旺财").start();//旺财
}
}
/*
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
*/
public class sleep_lainxi {
public static void main(String[] args) {
for (int i = 0; i <=10; i++) {
System.out.println(i);
try {
Thread.sleep(1000);//毫秒为单位 //【一秒打印一次i】
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*创建多线程
方式2:
实现Runnable接口
java.lang.Runable
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
java.lang.Thread类的构造方法:
Thread(Runnable target) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。
实现:
1、创建一个Runnable接口的实现类
2、在实现类中重写Runnable接口的run方法,设置线程任务
3、创建一个Runnable接口的实现类对象
4、创建Thread类对象,构造方法中传递Runable接口的实现类对象
5、调用Thread类中的start方法,开启新的线程执行run方法
实现Runnable接口创建多线程程序的好处:
1、避免了单继承的局限性,一个类可以继承多个类
2、降低了程序的耦合性,把设置线程任务和开启线程进行了分离(解耦)
实现类中重写了run方法来设置线程任务;创建Thread类对象,开启了新的线程
*/
public class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 5; i++) {
System.out.print(Thread.currentThread().getName()+"-->"+i);
}
}
}
public class main {
public static void main(String[] args) {
RunnableImpl run=new RunnableImpl();
Thread t=new Thread(run);
t.start();
for (int i = 0; i <= 5; i++) {
System.out.print(Thread.currentThread().getName()+"-->"+i);
}
}
}//main-->0Thread-0-->0main-->1Thread-0-->1main-->2Thread-0-->2main-->3Thread-0-->3main-->4Thread-0-->4main-->5Thread-0-->5
/*
1、同步代码块
原理:同步代码块传入锁对象,同步中的进程抢到CPU执行权后获取锁对象并执行,执行完后归还锁,不执行完不会释放锁;
同步外的线程就算抢到CPU执行权,没有锁对象也不会进入同步。
格式:synchronized(锁对象){
//可能出现线程安全的代码【共享数据的代码】
}
注意:1、通过代码块中的锁对象,可以使用任意对象
2、但是必须保证多个线程使用的锁对象是同一个
3、锁对象作用:
把同步代码块锁住,只让一个线程在同步代码块中执行
缺点:频繁的判断锁,获取锁,释放锁,程序效率会降低。
*/
public class RunnableImpl_01 implements Runnable {
private int ticket=100;
//创建一个锁对象
Object obj=new Object();
@Override
public void run() {
//使用死循环
while(true){
//创建同步代码块,让三个线程重复执行
synchronized (obj){
if(ticket>0){
//因为使共享数据,为了提高线程安全问题出现概率,让程序睡眠一下,此时线的程暂时失去程序的执行权
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
}
public class Ticket {
public static void main(String[] args) {
RunnableImpl_01 r=new RunnableImpl_01();
Thread t0=new Thread(r);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
/*2、同步方法:
1、把访问了共享数据的代码抽取出来,放到一个方法中
2、在方法上添加synchronized修饰符
格式:定义方法的格式
修饰符 synchronized 返回值类型 方法名(参数列表){
访问了共享数据的代码
}
同步方法把方法里的代码锁住,只让一个线程执行;同步方法的锁对象是实现类对象r,也就是this
*/
public class RunnableImpl_01 implements Runnable {
private int ticket=100;
@Override
public void run() {
while(true){
payTicket();
}
}
public synchronized void payTicket(){
if(ticket>0){
//因为使共享数据,为了提高线程安全问题出现概率,让程序睡眠一下,此时线程暂时失去程序的执行权
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
public class Ticket {
public static void main(String[] args) {
RunnableImpl_01 r=new RunnableImpl_01();
Thread t0=new Thread(r);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
/*3、Lock锁
3.1 java.util.concurrent.locks Lock接口
方法:void lock() 获取锁。
void unlock() 释放锁。
java.util.concurrent.locks.ReentrantLock implements Lock接口
使用步骤:1、在成员位置创建一个ReentrantLock对象
2、在可能会出现安全问题的代码前调用Lock接口中的方法lock() 获取锁
3、在可能会出现安全问题的代码后调用Lock接口中的方法unlock() 获取锁
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl_01 implements Runnable {
private int ticket=100;
//1
Lock l=new ReentrantLock();
@Override
public void run() {
while(true){
//2
l.lock();
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
//3
l.unlock();
}
}
}
public class Ticket {
public static void main(String[] args) {
RunnableImpl_01 r=new RunnableImpl_01();
Thread t0=new Thread(r);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
==
/*
3.2 最典型的代码如下:
class X {
private final ReentrantLock l = new ReentrantLock();
// ...
public void m() {
l.lock(); // block until condition holds
try {
// ... 无论程序是否异常都可以
} finally {
lock.unlock()
}
}
}
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl_01 implements Runnable {
private int ticket=100;
Lock l=new ReentrantLock();
@Override
public void run() {
while(true){
l.lock();
if(ticket>0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
l.unlock();//无论程序是否异常,都会把锁释放
}
}
}
}
}
public class Ticket {
public static void main(String[] args) {
RunnableImpl_01 r=new RunnableImpl_01();
Thread t0=new Thread(r);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t0.start();
t1.start();
t2.start();
}
}