Java 线程
一、程序,进程,线程
1)程序,进程,线程
程序
为完成特定的功能,使用计算机语言编写的一系列的指定集合,即静态代码
进程
进行中的程序,被加载到内存中,是操作系统分配资源的最小单位
线程
是进程中的最小的执行单元(任务),是操作系统进行任务调度的最小单元,隶属于进程。、
线程和进程的关系
1.一个线程只能属于一个进程,线程不能脱离进程
2.一个进程中至少有一个线程(主线程),java中的main()方法就是用来启动主线程
3.在主线程中可以创建并启动其他的线程
4.一个进程内的所有线程共享该进程的内存资源
二、创建线程
1)继承Thread 类的方式
Thread 类是java 提供的对线程进行管理的类
package com.xytx.javathread.threadtest1;
public class MyThread extends Thread{
//继承Thread 类
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程:"+i);
}
}
}
package com.xytx.javathread.threadtest1;
public class Test {
public static void main(String[] args) {
System.out.println("main:开始");
//创建线程对象
MyThread myThread = new MyThread();
//myThread.run(); 调用run(),不能启动线程,就是普通的方法调用
myThread.start();//启动线程
for (int i = 0; i < 1000; i++) {
System.out.println("main:"+i);
}
System.out.println("main结束");
}
}
2)实现Runnable 接口的方式
package com.xytx.javathread.threadtest2;
public class Test {
public static void main(String[] args) {
//创建一个线程要执行的任务
PrintNum printNum = new PrintNum();
//创建线程
Thread thread = new Thread(printNum);
thread.start();
Thread thread1 = new Thread(printNum);
thread1.start();
}
}
package com.xytx.javathread.threadtest2;
public class PrintNum implements Runnable{
//实现Runnable 接口
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
三、Thread 方法
Thread 方法
package com.xytx.javathread.threadway;
public class ThreadWay {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread();
thread.run();//线程任务执行的方法
thread.start();//启动线程
//Thread(target) 创建线程,并指定任务
//Thread(target,name) 创建线程,并指定任务,定义线程名称
Thread.currentThread();//获取到当前所在的线程
thread.getName();// setName() 获取线程的名称
thread.getPriority();//获得优先级
thread.setPriority(1);//设置优先级
/*
设置线程优先级
线程优先级默认是5,最大是10,最小是1
设置优先级后,不一定优先级高的,每一次都是有限执行,需要操作系统调用
*/
/*
重写run();方法中出现异常,不能抛出,必须处理
*/
Thread.sleep(1000);//让线程休眠指定时间(单位)
Thread.yield();//线程让步,主动让步 主动让出CPU执行权 可以直接进入到就绪队列中
thread.join();//等待该线程结束
}
}
守护线程 setDaemo
必须在启动线程之前设置守护线程,否则会出现异常:
IllegalThreadStateException 异常
当其他用户线程结束后,守护线程自动结束
如:垃圾回收器
四、 多线程
1)多线程
多线程:一个应用程序内部,可以同时执行多个任务
2)多线程优点
(1)提高程序处理能力,响应速度提高
(2)提高CPU利用率,压榨硬件的价值
(3)提升程序结构,将复杂任务分为多个线程,独立运行
3)多线程缺点
(1)对内存,CPU要求提高了,提升硬件性能改善
(2)多线程对同一个共享资源进行访问
(3)多线程需要协调和管理,所以需要CPU跟踪线程
4)线程同步
并发与并行
(1)并发:在同一个时间节点上,多个事情同时执行
(2)并行:在一个时间段内,多个事情依次执行
5)线程安全问题
多线程 + 访问同一个资源
6)解决办法
解决办法:排队+锁
模拟售票
- 继承Thread实现
/*
售票的线程
*/
public class TicketThread extends Thread{
static int num = 10;//票数 共享资源
static Object obj = new Object();//保证只有一个
@Override
public void run() {
while(true){
if(num>0){
print();
}else{
break;
}
}
}
/*
synchronized(同步监视器)在修饰方法时,同步对象(锁), 默认是this 一旦创建多个线程对象(只针对继承Thread类的方式)
static修饰后,同步对象变为 线程类(类只有一个)
*/
public static synchronized void print(){
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售出票:"+num);//0
//线程1再减之前 0
num--;
}
}
/* @Override
public void run() {
while(true){
//synchronized(同步锁,就是一个对象,但是要求对象只有一个)
synchronized(obj){
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售出票:"+num);//0
//线程1再减之前 0
num--;
}else{
break;
}
}//同步代码块,执行完成后 同步对象(锁) 会自动释放
}
}*/
}
public class Test {
public static void main(String[] args) {
TicketThread t1 = new TicketThread();
t1.setName("窗口1");
TicketThread t2 = new TicketThread();
t2.setName("窗口2");
t1.start();
t2.start();
}
}
- Runnable实现
public class TicketThread implements Runnable{
int num = 10;//共享变量
@Override
public void run() {
while(true){
if(num>0){
print();
}else{
break;
}
}
}
//synchronized 默认同步锁对象是this
public synchronized void print(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
}
/* @Override
public void run() {
while(true){
//添加同步对象,这种方式可以使用this来表示锁对象
synchronized (this){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}else{
break;
}
}
}
}*/
}
public class Test {
public static void main(String[] args) {
//创建出票任务
TicketThread ticketThread = new TicketThread();
//两个线程执行一个任务 num是只有一个的
Thread t1 = new Thread(ticketThread,"窗口1");
Thread t2 = new Thread(ticketThread,"窗口2");
t1.start();
t2.start();
}
}
五,Lock锁
- 从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
- ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加 锁、释放锁。
public class TicketThread implements Runnable{
int num = 10;//共享变量
Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
if(num>0){
print();
}else{
break;
}
}
}
public void print(){
//System.out.println("ssss");
try {
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();//在finally中释放 锁
}
//System.out.println("ssssssssssss");
}
}
public class Test {
public static void main(String[] args) {
//创建出票任务
TicketThread ticketThread = new TicketThread();
//两个线程执行一个任务 num是只有一个的
Thread t1 = new Thread(ticketThread,"窗口1");
Thread t2 = new Thread(ticketThread,"窗口2");
t1.start();
t2.start();
}
}
六,线程死锁
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步 资源,就形成了线程的死锁出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
- 设计时考虑清楚锁的顺序,尽量减少嵌套的加锁交互数量。
public class DieLock extends Thread{
static Object objA = new Object();
static Object objB = new Object();
boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){//线程1
synchronized (objA){
System.out.println("ifObjA");
synchronized (objB){
System.out.println("ifObjB");
}
}
}else{//线程2
synchronized (objB){
System.out.println("elseBbjB");
synchronized (objA){
System.out.println("elseObjA");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
DieLock dieLock1 = new DieLock(true);
DieLock dieLock2 = new DieLock(false);
dieLock1.start();
dieLock2.start();
}
}
七,线程通信
- 线程通信指的是对个线程通过消息传递实现相互牵制,相互调度,即线程间的相互作用。
- 设计三个方法:
.wait() 一旦执行此方法,当前线程就进入了阻塞状态,并释放同步监视器
.notify() 一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个
.notifuAll() 一旦执行此方法,就会唤醒所有被wait的线程 - 说明:
.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
.wait(),notify(),notifyAll()三个方法是定义在java.lang
.Object类中。
案例:消费者问题
/*
柜台 只有一个
*/
public class Counter {
int num = 0;
/*
添加
*/
// 同步对象时this 就是同一个Counter对象
public synchronized void add(){
if(num==0){
num++;
System.out.println("生产者线程生产了一个商品");
this.notify();//唤醒消费者
}else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
取
*/
public synchronized void sub(){
if(num>0){
num--;
System.out.println("消费者线程消费了一个商品");
this.notify();
}else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
消费者线程
*/
public class CustomerThread extends Thread{
Counter counter;
public CustomerThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.sub();
}
}
}
/*
生产者
*/
public class ProductorThread extends Thread{
Counter counter;
public ProductorThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.add();
}
}
public class Test {
public static void main(String[] args) {
Counter c = new Counter();//两个线程共享的资源 柜台
ProductorThread p = new ProductorThread(c);
CustomerThread cu = new CustomerThread(c);
p.start();
cu.start();
}
}
八,新增创建线程方式(Callable)
- 实现Callable接口与使用Runnable相比,Callable功能更强大些.
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,获取返回结果
public class MyThread implements Callable<Integer> {
/*
可以有返回值,可以泛型定义
可以抛出异常
*/
@Override
public Integer call() throws Exception {
int num = 0;
num = 10;
for (int i = 0; i <10 ; i++) {
num+=i;
}
return num;
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask(myThread);//添加
Thread t = new Thread(futureTask);
t.start();
try {
Integer res = futureTask.get();
System.out.println(res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}