进程
进程就是正在进行的程序,因为同一时间点计算机只能执行一个进程 ,计算机在不同的进程中快速的切换来达到多进程
多进程的意义:多进程的作用是提高CPU的使用率,而不是提高执行速度
线程
在一个进程中可以执行多个任务,而每一个任务可以看做一个线程
多线程的意义:多线程是为了提高应用程序的使用率,而不是提高执行速率
程序在运行过程中,都是在抢CPU的执行权,而且是随机的
并行和并发:
并行:某一时间段内同时运行多个程序
并发:某一时间点同时运行多个程序
启动JVM就是一个多线程的例子,会自动启动一个主线程,并用主线程调用main方法,所以多线程应该至少有一个主线程,一个垃圾回收线程
线程的两种开启方式
一,继承Thread类
package org.westos.demo2;
public class MyTest2 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
thread.setName("主线程");
System.out.println(thread.getName());
MyThread2 th1 = new MyThread2();
th1.setName("范冰冰");
MyThread2 th2 = new MyThread2();
th2.setName("李冰冰");
th1.start();
th2.start();
}
}
package org.westos.demo2;
public class MyThread2 extends Thread{
@Override
public void run() {//run方法中一般放比较费时的操作
super.run();
for(int i=0;i<100;i++){
//this.getName() 获取当前线程的名称
//Thread.currentThread() 获取当前正在执行的线程
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+i);
}
}
}
二,实现Rannable方法
package org.westos.demo;
public class MyTest {
public static void main(String[] args) {
MyThread th = new MyThread();
th.start();//开启方式1
//th.start(); 注意不要重复的去开启线程
//开启方式2:new Thread(new MyThread2()).start();
}
}
package org.westos.demo;
public class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("线程要执行的代码");
}
}
getName()获取当前线程的名称
currentThread()获取当前正在执行的线程
setPriority()设置线程优先级,括号中写优先级别1-10,默认优先级都是5
getPriority()获取线程优先级
sleep()线程休眠,括号中写休眠时长,毫秒,一般会出现异常,需要处理
join()线程加入,一般要在线程开启之后再加入,线程加入表示等待该线程执行完毕后,其他线程才能再次执行
yield()礼让线程,暂停正在执行的线程对象,并执行其他线程,这个方法必须在开启线程前调用
setDaemon() 设置为守护线程,这个方法必须在开启线程前调用
package org.westos.demo;
public class MyTest {
public static void main(String[] args) {
// 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
// 该方法必须在启动线程前调用。
String name = Thread.currentThread().getName();
System.out.println(name);
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
th1.setName("关羽");
th2.setName("张飞");
th1.setDaemon(true);//将该线程设置为守护线程
th2.setDaemon(true);
th1.start();
th2.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + "===" + i);
}
}
}
interrupt() 打断线程阻塞状态,会抛出异常
采用匿名内部类的方法开启线程
package org.westos.demo91;
public class MyTest91 {
public static void main(String[] args) {
//采用匿名内部类的方式来开启线程
//方式1
new Thread(){
@Override
public void run() {
super.run();
System.out.println("线程开启了");
}
}.start();
//方式2
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程2开启了");
}
}).start();
//Java1.8 Lambda表达式
Runnable ra=()->{
System.out.println("线程3开启了");
};
new Thread(ra).start();
new Thread(()->{
System.out.println("线程4开启了");
}).start();
}
}
案例:三个窗口同时出售100张票
package org.westos.demo92;
public class MyTest92 {
public static void main(String[] args) {
MyThread92 mt1 = new MyThread92();
MyThread92 mt2 = new MyThread92();
MyThread92 mt3 = new MyThread92();
Thread th1 = new Thread(mt1,"窗口1");
Thread th2 = new Thread(mt2,"窗口2");
Thread th3 = new Thread(mt3,"窗口3");
th1.start();
th2.start();
th3.start();
}
}
class MyThread92 implements Runnable {
private static int tickets = 100;//多个线程共享数据
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + (tickets--) + "张票");
}
}
}
}
可能产生数据安全的条件
一,多线程环境
二,有共享数据
三,有多条语句共同操作共享数据
处理数据安全有三种方法
1,加同步代码块(锁对象可以使用任意对象,但锁对象要让多个线程共享)
package org.westos.demo4;
public class MyThread4 implements Runnable{
private static int piao=100;//共享数据,多个线程共享此数据
private static final Object obj=new Object();//锁对象要共享
@Override
public void run() {
//同步代码块:参数里面要一个锁,这个锁传任意一个对象 注意 加的这个锁对象,一定要让多个线程去共享
while (true) {
//1 2 3
synchronized (obj) {//同步代码块 参数就是一个锁对象
//单线程环境
try {
//模拟网络延迟
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(piao>0) {
System.out.println(Thread.currentThread().getName()+"正在出售"+(piao--)+"张票");
}
}
}
}
}
2,使用同步方法(锁所对象是this)
3,静态同步方法(锁对象是当前类的字节码文件对象)
package org.westos.demo5;
public class MyThread5 implements Runnable {
private static int piao = 100;// 共享数据,多个线程共享此数据
private static final Object obj = new Object();// 锁对象要共享
int n = 0;
@Override
public void run() {
// 同步方法:他的锁对象是this
//静态同步方法的锁对象是:当前类的字节码文件对象
while (true) {
if (n % 2 == 0) {
synchronized (MyThread5.class) {// 同步代码块 参数就是一个锁对象
// 单线程环境
try {
// 模拟网络延迟
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (piao > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + (piao--) + "张票");
}
}
} else {
//maipiao();
maipiao2();
}
// 1 2 3
n++;
}
}
//静态同步方法
private static synchronized void maipiao2() {
// 单线程环境
try {
// 模拟网络延迟
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (piao > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + (piao--) + "张票");
}
}
// 同步方法:将 synchronized 加到方法上
private synchronized void maipiao() {
// 单线程环境
try {
// 模拟网络延迟
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (piao > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + (piao--) + "张票");
}
}
}
4,上锁和解锁
package org.westos.demo6;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread6 implements Runnable{
private static int piao=100;//共享数据,多个线程共享此数据
Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁
try {
//模拟网络延迟
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(piao>0) {
System.out.println(Thread.currentThread().getName()+"正在出售"+(piao--)+"张票");
}
//释放锁
lock.unlock();
}
}
}
死锁现像
出现同步嵌套,就容易产生死锁现象,两个或两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象
package org.westos.demo7;
public interface MyLock {
//定义两个锁对象
public static final Object objA=new Object();
public static final Object objB=new Object();
}
package org.westos.demo7;
public class MyTest7 {
public static void main(String[] args) {
MyThread7 th1 = new MyThread7(true);
MyThread7 th2 = new MyThread7(false);
th1.start();
th2.start();
}
}
package org.westos.demo7;
public class MyThread7 extends Thread {
boolean flag = true;
public MyThread7(boolean flag) {
super();
this.flag = flag;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
if (flag) {
synchronized (MyLock.objA) {
System.out.println("第一层进来了" + "A");
synchronized (MyLock.objB) {
System.out.println("第二层进来了" + "B");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("第一层进来了" + "B");
synchronized (MyLock.objA) {
System.out.println("第二层进来了" + "A");
}
}
}
}
}
线程池:高效的管理多个线程的容器
JDK1.5之后用Executors工厂类来生产线程池,Executors有以下几个方法
1。 public static ExecutorsService newCachdThreadPool()
根据任务的数量来创建对应的线程个数
package org.westos.demo;
import java.util.concurrent.ExecutorService;
public class MyRannable implements Runnable {
ExecutorService single;
public MyRannable(ExecutorService single) {
super();
this.single=single ;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"需要执行的代码");
}
}
package org.westos.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test3 {
public static void main(String[] args) {
ExecutorService cached = Executors.newCachedThreadPool();
//根据任务的数量来创建线程对应的线程个数
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.submit(new MyRannable(cached));
cached.shutdown();
}
}
2。 public static ExecutorsService newFixedThreadPool(int nThreads)
固定初始化几个线程
package org.westos.demo;
import java.util.concurrent.*;
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//固定初始化几个线程
ExecutorService fix = Executors.newFixedThreadPool(3);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + "任务执行了");
return "任务执行完了";//Callable接口会返回执行结果
}
};
Future<String> submit1 = fix.submit(callable);
Future<String> submit2 = fix.submit(callable);
Future<String> submit3 = fix.submit(callable);
Future<String> submit4 = fix.submit(callable);
Future<String> submit5 = fix.submit(callable);
Future<String> submit6 = fix.submit(callable);
String s = submit1.get();
System.out.println(s+"hahaha");
fix.shutdown();
}
}
3。 public static ExecutorsService newSingleThreadPool()
初始化一个线程的线程池
package org.westos.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//初始化一个线程的线程池
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务执行了");
}
});
single.shutdown();
}
}
Callable和Runnable的区别 相同点: 两者都是接口 两者都可用来编写多线程程序 两者都需要调用Thread.start()启动线程 不同点: 两者最大的不同点是 实现Callable接口的任务线程能返回执行结果 而实现Runnable接口的任务线程不能返回结果 Callable接口的call()方法允许抛出异常 而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛