多线程

关于多线程的实现方式,个人的看法,往后会逐步完善
关于多线程三大要点,一是synchronizer,二是同步容器,三是threadpool,executor
1,使用synchronized,但是这种方式效率稍微底下,但是既有可见性又有读写安全性
2,使用AtomicInteger这部分类型的数据做运算,缺点是此数据不可和其他判断同时使用,不然还是线程不安全
3,还有一个需要注意的是如果线程处理的时候抛异常会导致全部线程都释放,数据就乱了,所以一定要做好try catch准备
4,使用volatile使其有可见性
5,wait和notify或者notifyall,必须wait以后才能把锁让出去
6,CountDownLatch可以用于设置两个线程间先后关系的方式,在构造里设置减的次数,没执行一次down,就减一,知道变成零就执行在等待的线程,但是需要注意的是只能用一次
7,cyclicbarrier,使用方法是通过构造设置同时启动线程的条数,比如设置三条,然后当启动的时候,是分别start,但是不执行,只有当三条线程数满了,才同时执行。
8,用ReentrantLock来控制锁或释放,lock就是锁住相同一个锁的其他线程,只允许一个通过,完事了以后需要手动释放unlock.然后才轮到其他线程,还有trylock,还有interrupt可以打断等待的线程
9,使用condition实现对进入当前lock锁,得到当前锁的线程的控制,有await,signal和signalAll
10,关于threadLocal的使用,每个线程之间的数据隔离无关联
11,灵活使用Queue,vector等并发容器
队列有
12,concurrentLinkedQueue,不允许装空值,有空指针异常,poll没值了返回null,take没值了会阻塞,peek只是读取不删除,offer是添加不成功返回null,add是报错
13,blockingQueue阻塞式队列,put满了就会阻塞
14,ArrayListBlockingQueue
15,delayQueue(可以做定时任务)
16,transferQueue,此容器用于更高并发,特有的transfer方法用于直接拿数据给消费者,如果没有消费者则一直阻塞
17,synchronusQueue只能容纳一个值
Map有
18,并发比较高用concurrentHashMap,并发高且需要排序用concurrentSkipListMap,
19,CopyOnWrite是写的效率非常低,读的效率非常高,用的时候视情况而定,
20,还有collections.synchronizedXXXX(入参)这种工具创建有锁容器,但是效率低
21,Executor里有个executer方法用作执行线程
22,executerService的submit有callable方法和runable方法
23,Executors可以创建各种线程池
24,futrueTask内部有callable方法,设置返回值,放到thread方法里作入参可以执行
25,创建任务,就写一个类,实现callable或者runable方法,
newCachedThreadPool缓存型线程池,newFixedThreadPool 固定线程数线程池,newScheduledThreadPool定时任务线程池 ,newSingleThreadExecutor 单线程池newWorkstealingPool,精灵线程,forkJoinPool其中精灵线程也叫守护线程,即使服务器关了也一直在执行除非电脑主机关机才会停止

重点来了,关于在项目里如何使用多线程,我个人有自己总结的一套东西

如果是爬虫用多线程,在main方法中使用的话
仿照下访代码即可,这是我曾经写过的一个爬虫,代码比较简陋粗暴,直接new Thread

public class taskone {

	public static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue(200);

	public static NewsDao newsdao=new NewsDao();
	
	public static void main(String[] args) throws Exception {
		
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				while (true) {
					try {
						// System.out.println("剩余解析数量" + queue.size());
						String j = queue.take();
						nextPage(j);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}).start();
		}

		String url = "http://www.xxxxxx.com//news/news_list_index.do";
		List<NameValuePair> list = new LinkedList<>();
		BasicNameValuePair param1 = new BasicNameValuePair("pageSize", "10");
		BasicNameValuePair param2 = new BasicNameValuePair("pageNum", "1");
		BasicNameValuePair param3 = new BasicNameValuePair("newsCategory", "196");
		list.add(param1);
		list.add(param2);
		list.add(param3);
		UrlEncodedFormEntity entity1 = null;
		entity1 = new UrlEncodedFormEntity(list, "UTF-8");
		HttpPost httpget = new HttpPost(url);
		httpget.setEntity(entity1);
		CloseableHttpClient createDefault = HttpClients.createDefault();
		CloseableHttpResponse execute = createDefault.execute(httpget);
		if (200 == execute.getStatusLine().getStatusCode()) {
			HttpEntity entity = execute.getEntity();
			String string = EntityUtils.toString(entity, Charset.forName("utf-8"));
			int i = string.indexOf("[");
			int j = string.indexOf("]");
			String substring = string.substring(i, j + 1);
			Gson gson = new Gson();
			ArrayList<Map> fromJson = gson.fromJson(substring, ArrayList.class);
			for (Object map : fromJson) {
				Map<String, Double> map1 = (Map) map;
				Double double1 = (Double) map1.get("id");
				String string2 = double1.toString();
				if (string2.contains(".0")) {
					string2 = string2.substring(0, string2.length() - 2);
				}
				string2 = "http://www.zj998.com//news/info_" + string2 + ".html";
			}
			addpage();
		}
	}

	public static void addpage() throws InterruptedException {
		for (Integer i = 1; i < 105; i++) {
			queue.put(i.toString());
		}
	}

	public static void nextPage(String i) throws Exception {
		String url = "http://www.xxxxxx.com//news/news_list_index.do";
		List<NameValuePair> list = new LinkedList<>();
		BasicNameValuePair param1 = new BasicNameValuePair("pageSize", "10");
		BasicNameValuePair param2 = new BasicNameValuePair("pageNum", i);
		BasicNameValuePair param3 = new BasicNameValuePair("newsCategory", "196");

		list.add(param1);
		list.add(param2);
		list.add(param3);
		UrlEncodedFormEntity entity1 = null;
		entity1 = new UrlEncodedFormEntity(list, "UTF-8");
		HttpPost httpget = new HttpPost(url);
		httpget.setEntity(entity1);
		CloseableHttpClient createDefault = HttpClients.createDefault();
		CloseableHttpResponse execute = createDefault.execute(httpget);
		if (200 == execute.getStatusLine().getStatusCode()) {
			HttpEntity entity = execute.getEntity();
			String string = EntityUtils.toString(entity, Charset.forName("utf-8"));
			int k = string.indexOf("[");
			int j = string.indexOf("]");
			String substring = string.substring(k, j + 1);
			Gson gson = new Gson();
			ArrayList<Map> fromJson = gson.fromJson(substring, ArrayList.class);
			for (Object map : fromJson) {
				Map<String, Double> map1 = (Map) map;
				Double double1 = (Double) map1.get("id");
				String string2 = double1.toString();
				if (string2.contains(".0")) {
					string2 = string2.substring(0, string2.length() - 2);
				}
				string2 = "http://www.xxxxx.com//news/info_" + string2 + ".html";
				System.out.println(string2);
				newsdao.save(string2);
			}
		}

	}

}

如果是处理逻辑稍微复杂点的
1,创建threadTask,实现runable接口,在run方法中写下每单次执行的逻辑,比如

@Component
public class ThreadTask implements Runnable {
	
	@Override
	public void run() {
	Queue<String> q = QueBean.getQue();
		synchronized (this) {
		while (q.size() > 0) {
		System.out.println("开始执行");
		String take = q.poll();
		System.out.println("执行了" + take + Thread.currentThread());
		}
	}
}

2,再创建一个单例模式的容器

public class QueBean implements Serializable {

	private static class bean {
		private static Queue<String> q = new ConcurrentLinkedQueue<String>();
	}

	public static Queue<String> getQue() {
		return bean.q;
	}

}

3,在service内写下添加数据到队列的代码,注入线程服务类

@Service
public class ThreadService {

	@Autowired
	private ThreadTask threadTask;

	public void demo1() {
		Queue<String> que = QueBean.getQue();
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 1000000; i++) {
			System.out.println(que + "添加");
			que.add(i + "I");
			newFixedThreadPool.execute(threadTask);
		}
	}
}

搞定

最后补充一句,多线程中,如果需要对异常进行处理,就设置UncaughtExceptionHandler,或者DefaultUncaughtExceptionHandler,
这样当抛异常的时候,就可以准确知道抛异常的是哪个线程和异常的原因

t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			public void uncaughtException(Thread t, Throwable e) {
				System.out.println("线程:" + t.getName() + " 出现了异常:");
				e.printStackTrace();
			}
		});

或者

Demo11_MyThread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			
			
			public void uncaughtException(Thread t, Throwable e) {
				System.out.println(" 线程:" + t.getName() + " 出现了异常:" );
				e.printStackTrace();
			}
		});
		
public class bb {

	static class MyUncauthExceptionHandler implements Thread.UncaughtExceptionHandler {

		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("UncaughtException:" + t.getName() + " " + e.getMessage());
		}
	}

	public static void main(String[] args) {

		Queue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10000000);
		for (int i = 1; i < 10000000; i++) {
			blockingQueue.add(i);
		}

		new Thread(() -> {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Thread.currentThread().setName("thread1");
			Thread.setDefaultUncaughtExceptionHandler(new MyUncauthExceptionHandler());
			while (blockingQueue.size() > 0) {
				Integer poll = blockingQueue.poll();
				System.out.println(poll);
				if (poll == 500) {
					int j = 1 / 0;
					// TODO: handle exception
				}
			}
		}).start();
		new Thread(() -> {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Thread.currentThread().setName("thread2");
			Thread.setDefaultUncaughtExceptionHandler(new MyUncauthExceptionHandler());
			while (blockingQueue.size() > 0) {
				Integer poll = blockingQueue.poll();
				System.out.println(poll);
				if (poll == 501) {
					int j = 1 / 0;
				}
			}
		}).start();
	}
}

也可以这样

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int vipLogToReqOrder() {
        Queue<Integer> ok = new ArrayBlockingQueue<>(9999);
        List<VipOperateLog> vipOperateLogs1 = vipOperateLogMapper.findAll();
        synchronized (this) {
            vipOperateLogs.addAll(vipOperateLogs1);
        }
        System.out.println("总共" + vipOperateLogs.size() + "条数据");
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 6, 2, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1),
                new ThreadPoolExecutor.AbortPolicy());
        // ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(6);
        for (int j = 0; j < 10; j++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    while (vipOperateLogs.size() > 0) {
                        VipOperateLog e = vipOperateLogs.poll();
                        if (e == null) {
                            break;
                        }
                     // 业务代码
              
                        System.out.println("还剩" + vipOperateLogs.size() + "条数据");
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            });
        }
        for (Integer i : ok) {
            if (i <= 0) {
                throw new RuntimeException("跑错");
            }
        }
        return 1;
    }

   ExecutorService executorService= Executors.newFixedThreadPool(4);
     Future<String> submit = executorService.submit(() -> {
         return "hello";
     });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值