转载声明:文章来源https://blog.csdn.net/qq_38553011/article/details/141426338
一、进程和线程
1.进程
1) 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动
是系统进行资源分配的基本单位(简单解释为程序的执行过程)
2) 独立性:每一个进程都有自己的空间,在没有经过进程本身允许的情况下,一个进程不可以直接访问其它的的进程空间
动态性:进程是动态产生,动态消亡的
并发性:任何进程都可以同其它进程一起并发执行
3) 多进程同时工作,对于CPU而言,它是在多个进程间轮换执行的。
2.线程
1) :进程可以同时执行多个任务,每个任务就是线程
2) 多线程的意义:随着处理器上的核心数量越来越多,现在大多数计算机都比以往更加擅长并行计算,一个线程,在一个时刻, 只能运行在一个处理器核心上;提高执行效率;同时处理多个任务
二、Java开启线程的方式
1.继承Thread类
1)编写一个类继承Thread
2) 重写run方法
3) 将线程任务代码写在run方法中
4) 创建线程对象
5) 调用start方法开启线程
细节:调用start方法开启线程,会自动调用run方法执行
注意:只有调用start方法,才是开启了新的线程
public static void main(String[] args) {
myTheard mt1 = new myTheard();
myTheard mt2 = new myTheard();
mt1.start();
mt2.start();
}
class myTheard extends Thread{
public void run(){
for (int i = 0; i < 200; i++) {
System.out.println("任务"+i);
}
}
}
JAVA程序默认是多线程的,程序启动后默认会有两个线程:主线程和垃圾回收线程
2.实现Runnable接口 (扩展性更强,重点掌握)
1) 编写一个类实现Runnable接口
2) 编写run方法
3) 将线程任务写进run方法中
4) 创建线程任务资源
5) 创建线程对象,将资源传入
6) 使用线程对象调用start方法开启线程
class myRunnable implements Runnable{//1.编写一个类实现Runnable接口
@Override
public void run(){//2.编写run方法
for (int i = 0; i < 200; i++) {
System.out.println("任务"+i);//3.将线程任务写进run方法中
}
}
}
public static void main(String[] args) {
//4.创建线程任务资源
myRunnable mr = new myRunnable();//这个不能直接start,要作为资源传入Thread中
//5.创建线程对象,将资源传入
Thread t = new Thread(mr);
//6.使用线程对象调用start方法开启线程
t.start();
//主线程任务,注意要在start开启线程的下面,否则不会出现多线程交替执行,因为没有新开的线程
for (int i = 0; i < 2000; i++) {
System.out.println("main主线任务"+i);
}
}
3.实现callable接口 (有返回值,重点掌握)
1) 创建一个类,实现Callable接口,有泛型,根据返回值定义
2) 重写call方法
3) 将线程任务写在call方法中
4) 创建线程任务资源
5) 创建线程任务对象
6) 创建线程对象,将资源传入
7) 使用线程对象调用start方法开启线程
//1.创建一个类,实现Callable接口,有泛型,根据返回值定义
class myCallable implements Callable<Integer>{
@Override //2.重写call方法
public Integer call() throws Exception {
//3.将线程任务写在call方法中
int sum=0;
for (int i = 0; i <= 100; i++) {
sum += i;
System.out.println("sum:"+ sum);
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//4.创建线程任务资源
myCallable mc = new myCallable();
//5.创建线程任务对象
FutureTask<Integer> ft1 = new FutureTask<>(mc);
FutureTask<Integer> ft2 = new FutureTask<>(mc);
//6.创建线程对象,将资源传入
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
//7.使用线程对象调用start方法开启线程
t1.start();
t2.start();
//在做线程对比的时候一定要定义两个,否是在一个线程中拿值
final Integer con1 = ft1.get();
final Integer con2 = ft2.get();
System.out.println(con1);
System.out.println(con2);
}
三、线程相关方法
线程的调度方式:
1)抢占式调度(具有随机性)
2)非抢占式调度(轮流使用)
1.继承Thread类
class myTheard extends Thread{
public myTheard(String name) {//这里是带参构造方法
super(name);
}
public void run(){
for (int i = 0; i < 200; i++) {
//通过getName 获取当前流的名称
System.out.println(super.getName()+"===任务"+i);
}
}
}
private static void threadText() {
//选择带参构造可以在创建的时候传相应的字符串名称
myTheard mt1 = new myTheard("A");
myTheard mt2 = new myTheard("B");
//下面是通过setName 对流进行命名的
//mt1.setName("A");
// mt2.setName("B");
mt1.start();
mt2.start();
}
2.实现Runnable接口
class myRunnable implements Runnable{//1.编写一个类实现Runnable接口
@Override
public void run(){//2.编写run方法
for (int i = 0; i < 200; i++) {
//在这里由于实现的是接口,所以不能直接super.getName(),需要先获取到当前流,再进行getName()
System.out.println(Thread.currentThread().getName()+"任务"+i);//3.将线程任务写进run方法中
}
}
}
private static void runnableText() {
myRunnable mr = new myRunnable();//这个不能直接start,要作为资源传入Thread中
Thread t = new Thread(mr,"自定义流:");
//可以通过setName设置
//t.setName("A");
t.start();
//主线程任务,注意要在start开启线程的下面,否则不会出现多线程交替执行,因为没有新开的线程
for (int i = 0; i < 2000; i++) {
System.out.println(Thread.currentThread().getName() +i);
}
}
3.实现callable接口
class myCallable implements Callable<Integer>{
@Override //2.重写call方法
public Integer call() throws Exception {
//3.将线程任务写在call方法中
int sum=0;
for (int i = 0; i <= 100; i++) {
sum += i;
System.out.println(Thread.currentThread().getName() + "sum:"+ sum);
}
return sum;
}
}
private static void callableText() throws InterruptedException, ExecutionException {
//4.创建线程任务资源
myCallable mc = new myCallable();
//5.创建线程任务对象
FutureTask<Integer> ft1 = new FutureTask<>(mc);
FutureTask<Integer> ft2 = new FutureTask<>(mc);
//6.创建线程对象,将资源传入
Thread t1 = new Thread(ft1,"A");
Thread t2 = new Thread(ft2,"B");
//t1.setName("A");
//t2.setName("B");
//7.使用线程对象调用start方法开启线程
t1.start();
t2.start();
//在做线程对比的时候一定要定义两个,否是在一个线程中拿值
final Integer con1 = ft1.get();
final Integer con2 = ft2.get();
System.out.println(con1);
System.out.println(con2);
}
四、线程安全和同步
1.安全问题出现的条件:
是多线程环境
有共享数据
有多条语句操作共享数据
2.同步技术 :
将多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程可以执行
同步代码块
synchronized(锁对象){ 多条语句操作共享数据的代码}
注意:锁对象可以是任意对象,但是需要保证多条线程的锁对象是同一把锁。通常锁对象用类的编译类。同步可以解决多线程的数据安全问题,但是也会降低程序的运行效率。
例子:3条线程共计执行2000次
class ExecuteRun implements Runnable{
@Override
public void run() {
int sum=2000;
synchronized (ExecuteRun.class){
while (true){
if (sum == 0){
break;
}
System.out.println(Thread.currentThread().getName() + "倒数第" + sum + "次");
sum--;
}
}
}
}
private static void synText1() {
ExecuteRun er = new ExecuteRun();
Thread t1 = new Thread(er,"线程A:");
Thread t2 = new Thread(er,"线程B:");
Thread t3 = new Thread(er,"线程C:");
t1.start();
t2.start();
t3.start();
}
上面的代码我们是通过是实现Runnable类来实现的,资源都是一份,下面直接通过Thread实现:
class ExecuteThreat extends Thread{
public ExecuteThreat(String name) {
super(name);
}
private static int sum=2000;//要在自定义变量前面增加static关键字,保证资源的共享
public void run(){
synchronized (ExecuteThreat.class){
while (true){
if (sum == 0){
break;
}
System.out.println(getName() + "倒数第" + sum + "次");
sum--;
}
}
}
}
private static void synTheart() {
//这是李new了三次
ExecuteThreat t1 = new ExecuteThreat("线程A:");
ExecuteThreat t2 = new ExecuteThreat("线程B:");
ExecuteThreat t3 = new ExecuteThreat("线程C:");
t1.start();
t2.start();
t3.start();
}
同步方法
在方法的返回值类型前面加入synchronized 关键字
public synchronized void method(){}
注意:(可以出面试题)
方法分为静态和非静态
静态方法的锁对象是字节码对象,非静态方法的锁对象是this
class ExecuteRun implements Runnable{
private static int sum=2000;
@Override
public void run() {
while (true){
if (model1()) break;
}
}
//在方法前面上synchronized关键字
private synchronized static boolean model1() {
if (sum == 0){
return true;
}
System.out.println(Thread.currentThread().getName() + "倒数第" + sum + "次");
sum--;
return false;
}
}
Lock锁
Lock 是接口,无法直接创建对象
class ExecuteRun implements Runnable{
private static int sum=2000;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if (sum == 0){
break;
}
System.out.println(Thread.currentThread().getName() + "倒数第" + sum + "次");
sum--;
} finally {
lock.unlock();
}
}
}
}
注意:unlock释放的位置,如果不用try/finally,需要在break前面在释放一次,要不然代码不会停止运行
死锁
介绍:由于两个或者多个线程互相持有对方所需要的的资源,导致这些线程处于等待状态,无法前往执行
产生死锁的情况:同步嵌套
帖子还没人回复快来抢沙发