java 多线程 并发编程_使用多线程和并发编程(一)

一.先初步了解一下基本的概念进程:在一个操作系统中,每个独立执行的程序都可以是一个进程。线程:一个程序至少有一个进程,一个进程至少有一个线程,java里有一个主线程和垃圾回收线程。线程的3中创建方式:1.继承Thread类2.实现Runnable接口3.实现Callable接口,和Future、线程池一起使用线程的优先级:优先级的返回是1-10,默认是5,数越大优先级越高。join的作用是:等待该线程终止,指的是主线程等待子线程的终止。子线程调用了join方法,只有子线程执行完之后才会调用主线程。(主线程需要用到子线程的处理结果是使用join)二.多线程之间的状态转换

330c2beb-4f32-40b7-8124-adc8f4456b84.png

状态之间装换的demo:

这个例子里面涉及到了线程的创建,运行,堵塞,等待,锁池等状态,主要是为了加深自己对状态的理解。

package cn;

public class ThreadDemo extends Thread {

//标识

private boolean runningFlag=false;

public ThreadDemo(){

runningFlag = false;

}

public synchronized void setRunningFlag(boolean runningFlag) {

this.runningFlag = runningFlag;

if(runningFlag){

this.notify();

}else{

try {

System.out.println("线程"+Thread.currentThread().getName()+"开始等待");

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

@Override

public void run() {

sayHello();

}

//锁池状态

public synchronized void sayHello(){

while(true){

if(!runningFlag){

try {

System.out.println("线程"+Thread.currentThread().getName()+"开始等待");

this.wait();//等待状态

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}else{

try {

sleep(3000);//堵塞状态

System.out.println("线程"+Thread.currentThread().getName()+"任务完成\n");

setRunningFlag(false); //让当前线程处于等待任务状态

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

public static void main(String[] args) {

int i=10;

while (i-->0) {

//新建创建

ThreadDemo demo=new ThreadDemo();

demo.setName("demo"+i);

demo.setRunningFlag(true);

//可运行状态,start之后等待cpu获取时间片

demo.start();

}

}

}

三.使用4个线程分别计算1-25,26-50,51-75,76-100的值,最后求和。

实现思路:定义4个线程分别计算每份数据的和,然后把求的和加入到集合中,最后对集合中的值进行相加。

使用技术:使用ExecutorService、Callable、Future实现有返回结果的多线程。

shutdown:调用后,不可以再submit新的task,已经submit的将继续执行。

shutdownNow:试图停止当前正执行的task,并返回尚未执行的task的list

package cn;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class ThreadAddFuture {

//创建多个有返回值的任务

public static List futureList=new ArrayList();

public static void main(String[] args) throws InterruptedException, ExecutionException {

int sum=0;

int taskSize=4;

ThreadAddFuture add=new ThreadAddFuture();

//创建一个线程池有4个

ExecutorService pool=Executors.newFixedThreadPool(taskSize);

for (int i =1; i <=76;) {

ThreadTest thread=add.new ThreadTest(i,i+24);

//执行任务并获取Future对象

Future future=pool.submit(thread);

//添加到futureList

futureList.add(future);

//正好实现4个线程分别计算1-25|26-50|51-75|76-100的和

i+=25;

}

//获取所有并发任务的返回结果

if(futureList!=null && futureList.size()>0){

for(Future future:futureList){

sum+=(Integer)future.get();

}

}

System.out.println("total result: "+sum);

//关闭线程池

pool.shutdown();

}

//实现Callable接口

class ThreadTest implements Callable{

private int begin;

private int end;

private int sum=0;

public ThreadTest(int begin,int end){

this.begin=begin;

this.end=end;

}

@Override

public Integer call() throws Exception {

for (int i =begin; i <=end; i++) {

sum+=i;

}

System.out.println("from "+Thread.currentThread().getName()+" sum="+sum);

return sum;

}

}

}

四.使用runnable、CountDownLatch、线程池的demoCountDownLatch:CountDownLatch是一个同步的辅助器是一个计数器,只要计数器为0主线程就可以结束堵塞进行执行,和join很像但是比join更加灵活。await:await() 方法会一直阻塞直到计数器为0,主线程才会继续往下执行。countDown():countDown() 方法将计数器减1。CountDownLatch和join的区别:调用thread.join() 方法必须等thread 执行完毕,当前线程才能继续往下执行,而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。

package cn;

import java.util.UUID;

import java.util.concurrent.Callable;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class WatchThread {

private String name=UUID.randomUUID().toString();

public void testThread() throws InterruptedException{

int threadNum=10;

//创建计数器

CountDownLatch threadSignal=new CountDownLatch(threadNum);
//创建线程池

ExecutorService executor=Executors.newFixedThreadPool(threadNum);

for (int i = 0; i

TestThread task=new TestThread(threadSignal);

//执行任务

executor.execute(task);

}
//堵塞线程

threadSignal.await();
//关闭线程池

executor.shutdown();

System.out.println(Thread.currentThread().getName() + "+++++++结束.");

}

public static void main(String[] args) throws InterruptedException{

WatchThread test=new WatchThread();

test.testThread();

}

private class TestThread implements Runnable{

private CountDownLatch threadsSignal;

public TestThread(CountDownLatch threadsSignal){

this.threadsSignal=threadsSignal;

}

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + "开始..." + name);

System.out.println("开始了线程::::" + threadsSignal.getCount());

threadsSignal.countDown();//必须等核心处理逻辑处理完成后才可以减1

System.out.println(Thread.currentThread().getName() + "结束. 还有"

+ threadsSignal.getCount() + " 个线程");

}

}

}

五.模拟实现消息的发送,一个服务端,一个客户端。

实现思路:

发送的话,要保证信息100%的发送给客户端,那么发给客户端之后,客户端返回一个消息告诉服务器,已经收到。当服务器一直没有收到客户端返回的消息,那么服务器会一直发 送这个信息,直到客户端发送回确认信息,这时候再删除重复发送的这个信息。

在服务端创建一个ConcurrentHashMap,当客户端正确接收服务端发送的数据并返回成功标识,从ConcurrentHashMap中删除成功的消息。

1.创建PushThread类向客户端发送数据

package cn1;

import java.util.Map.Entry;

public class PushThread extends Thread{

//发送代码,是不断遍历内存对象councurrenthashmap,从中取出信息,不断的重发

@Override

public void run() {

try {

//重发消息

for(Entry hashMap:MainThread.pushmessage.entrySet()){

System.out.println("消息id:"+hashMap.getKey()+"未发送成功,在此重发:"+hashMap.getValue());

}

sleep(1000);

} catch (Exception e) {

e.printStackTrace();

}

}

}

2.创建RemoveThread类接收客户端返回的数据,成功从ConcurrentHashMap中删除消息

package cn1;

import java.util.Map.Entry;

public class RemoveThread extends Thread {

//循环的接收客户端返回的消息,返回成功从concurrentHashMap中删除

@Override

public void run() {

try {

for (int i = 0; i < 10000; i++) {

sleep(2000);

for(Entry map:MainThread.pushmessage.entrySet()){

if (map.getKey()==i) {

System.out.println("成功收到id为:"+map.getKey()+"返回的信息,删除该元素");

MainThread.pushmessage.remove(map.getKey());

}

}

System.out.println("内存对象中的元素数量为:"+MainThread.pushmessage.size());

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

3.创建MainThread类,向concurrentHashMap中添加测试数据

package cn1;

import java.util.concurrent.ConcurrentHashMap;

public class MainThread {

/**

* 消息的发送,一个是服务器,一个是客户端。发送的话,要保证信息100%的发送给客户端,那么发给客户端之后,客户端返回一个消息告诉服务器,已经收到。当服务器一直没有收到客户端返回的消息,那么服务器会一直发送这个信息,直到客户端发送回确认信息,这时候再删除重复发送的这个信息。

*/

public static ConcurrentHashMap pushmessage=new ConcurrentHashMap();

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

pushmessage.put(i, "该消息是id为"+i+"的消息");

}

Thread pushThread=new PushThread();

Thread remove=new RemoveThread();

pushThread.start();

remove.start();

for (int i = 10; i < 20; i++) {

pushmessage.put(i, "又一波到来,消息是id为"+i+"的消息");

}

}

}

六.synchronized的实现原理

synchronized实现同步的基础:java中的每一个对象都可以是锁,主要有3种形式:

. 对于普通同步方法,锁的是实例对象。

.对于静态同步方法,锁的是当前类的Class对象。

. 对于同步代码块,锁的是括号里配置的对象。

JVM是基于进入和退出Moniter对象来实现同步和代码块同步的,同步代码块是基于monitorenter和monitorexit实现的,同步方法ACC_synchronized实现的,monitorenter放在同步代码块的开始位置,monitorexit放在同步代码块的结束位置,必须是成对出现的。当前一个moniter被持有后,它将处于锁定状态,线程执行到moniteorenter时,获得monitor的所有权,所得对象的锁。

synchronized获得的锁是存放在对象头里的,堆中的对象由对象头,实例变量和填充数据组成。对象头的markword存储的是HashCode、分代年龄和锁标记位,锁标记位有无锁状态、偏向锁、轻量级锁、重量级锁。对象头还会存储是否是偏向锁的标识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值